#include <iostream>
#include <stdlib.h>
#include <time.h>
#include "puzzle.h"

using namespace std;

/*------------------------------------------------
 *
 *        0    ->     0[ ]
 *       [ ]           
 *                    |
 *                    V
 *                     
 *       [G]   <-     0 
 *                   [ ]
 *
 * The above diagram shows the puzzle we are trying
 * to achieve in 2D, the code does this for 3D. 
 *------------------------------------------------*/
Puzzle::Puzzle(int numTurns, int numBlocksStraight)
{
    srand((int)time(0));

    blocks = new Brick[numTurns];
    rot[0] = rot[1] = false; //rot[0] xy rot, rot[1] zy rot

    Brick first = {0, 0, 0, false};
    Direction arr[4] = {LEFT, RIGHT, FORWARD, BACK};
    Direction prev = DOWN;
    blocks[0] = first;
    wc[0] = wc[1] = wc[2] = 0;
    solved = false;

    int i;
    for(i = 1; i < numTurns; i++)
    {

GETMOVE: //label to be used when the values got are negative. 
        Brick tmp = blocks[i-1]; 
        Direction curDir;
        //based on prev direction we limit the next direction that can be used
        switch(prev)
        {
            case UP:
            case DOWN:
                //chooose left right front back
                arr[0] = LEFT; arr[1] = RIGHT; arr[2] = FORWARD; arr[3] = BACK;  
                
                //handling the placement of successive bricks
                if(prev == UP)
                    tmp.pos[1] -= 1;
                else
                    tmp.pos[1] += 1; //(tmp.pos[1] > 0? tmp.pos[1] + 1 : tmp.pos[1] - 1);

                break;

            case LEFT:
            case RIGHT:
                //choose from up down forward back
                //arr[0] = UP; arr[1] = DOWN; arr[2] = FORWARD; arr[3] = BACK;  
                arr[0] = UP; arr[1] = UP; arr[2] = FORWARD; arr[3] = BACK;  

                //handling the placement of successive bricks
                if(prev == LEFT)
                    tmp.pos[0] += 1;//(tmp.pos[0] > 0? tmp.pos[0] + 1 : tmp.pos[0] - 1);
                else
                    tmp.pos[0] -= 1;//(tmp.pos[0] > 0? tmp.pos[0] -1 : tmp.pos[0] + 1);
                break;

            case FORWARD:
            case BACK:
                //choose from left right up down 
                arr[0] = LEFT; arr[1] = RIGHT; arr[2] = UP; arr[3] = DOWN;  

                //handling the placement of successive bricks
                if(prev == FORWARD)
                    tmp.pos[2] -= 1; //(tmp.pos[2] > 0? tmp.pos[2] - 1 : tmp.pos[2] + 1);
                else
                    tmp.pos[2] += 1; //(tmp.pos[2] > 0? tmp.pos[2] + 1 : tmp.pos[2] -1 );
                break;
        }

        curDir = arr[((int)(4.0 * rand()/(RAND_MAX + 1.0)))];

        switch(curDir)
        {
            case LEFT:
                tmp.pos[0] = blocks[i-1].pos[0] - numBlocksStraight; 
                             //(blocks[i-1].pos[0] > 0? numBlocksStraight : -numBlocksStraight);
                break;
            case RIGHT:
                tmp.pos[0] = blocks[i-1].pos[0] + numBlocksStraight;
                             //(blocks[i-1].pos[0] > 0? numBlocksStraight : -numBlocksStraight);
                break;

            case BACK:
                tmp.pos[2] = blocks[i-1].pos[2] - numBlocksStraight;
//                             (blocks[i-1].pos[2] > 0? numBlocksStraight : -numBlocksStraight);
                break;
            case FORWARD:
                tmp.pos[2] = blocks[i-1].pos[2] + numBlocksStraight;
//                             (blocks[i-1].pos[2] > 0? numBlocksStraight : -numBlocksStraight);
                break;

            case UP:
                tmp.pos[1] = blocks[i-1].pos[1] + numBlocksStraight;
//                             (blocks[i-1].pos[1] > 0? numBlocksStraight : -numBlocksStraight);
                break;
            case DOWN:
                tmp.pos[1] = blocks[i-1].pos[1] - numBlocksStraight; 
//                             (blocks[i-1].pos[1] > 0? numBlocksStraight : -numBlocksStraight);
                break;
        }
        if(tmp.pos[0] < 0 || tmp.pos[1] < 0 || tmp.pos[2] < 0)
        {
            goto GETMOVE;
        } 
        
        //update the world width, height
        if(wc[0] < tmp.pos[0])
            wc[0] = tmp.pos[0];
        if(wc[1] < tmp.pos[1])
            wc[1] = tmp.pos[1];
        if(wc[2] < tmp.pos[2])
            wc[2] = tmp.pos[2]; 

        //blocks.push_back(tmp);
        blocks[i] = tmp;
        prev = curDir; //update prev for next itiration
    }
    blocks[i-1].isFinal = true;

    //set the ball on top of the blocks
    i = 0;
    ballPos[0] = blocks[0].pos[0];
    ballPos[1] = blocks[0].pos[1] + 1;
    ballPos[2] = blocks[0].pos[2];

    while(true)
    {
        std::cout <<  blocks[i].pos[0] << " " <<  blocks[i].pos[1] << " " <<  blocks[i].pos[2] << std::endl;
        if(blocks[i].isFinal)
            break;
        i++;
    }
}

Brick* Puzzle::getBricks()
{
    return blocks;
}

float * Puzzle::getBallPos()
{
    return ballPos; 
}

void Puzzle::update()
{
    int i = 0;
    bool colliding = false;
    while(1)
    {
        //using 0.5 because each box is 1.0 wide and is centered at pos
        if( (ballPos[0] > (blocks[i].pos[0]-0.5f) && ballPos[0] < (blocks[i].pos[0]+0.5f)) && 
           (ballPos[2] > (blocks[i].pos[2]-0.5f) && ballPos[2] < (blocks[i].pos[2]+0.5f)) )
        {
            //the ball is in x range now check if it is touching the box or not.
            //centers should be exactly 1.0f apart if they are touching.
            if(ballPos[1] - blocks[i].pos[1] < 1.0f)
            {
                //the ball always falls down (change only y)
                //ball stops if block below it. 
                colliding = true;
                if(blocks[i].isFinal)
                    solved = true;
                break;
            }
        }
        if(blocks[i].isFinal)
            break;
        i++;
    }

    if(!colliding)
        ballPos[1]-=0.1;
}

void Puzzle::rotate(int i1, int i2, bool ccw)
{
    int i = 0;
    float w = (wc[i1] > wc[i2] ?  wc[i1] : wc[i2]);
    while(1)
    {      
        float x1 = blocks[i].pos[i2];
        float y1 =  w - blocks[i].pos[i1];
        if(ccw)
        {
            x1 =  w - blocks[i].pos[i2];
            y1 = blocks[i].pos[i1];
        } 
        blocks[i].pos[i1] = x1; 
        blocks[i].pos[i2] = y1;
        if(blocks[i].isFinal)
            break;
        i++;
    } 
    float x1 = ballPos[i2];
    float y1 = w - ballPos[i1];
    if(ccw)
    {
        x1 =  w - ballPos[i2];
        y1 = ballPos[i1];
    } 
 
    ballPos[i1] = x1;
    ballPos[i2] = y1;

}
 
void Puzzle::rotXY(bool ccw)
{
    rotate(0, 1, ccw);
}

void Puzzle::rotZY(bool ccw)
{
    rotate(2, 1, ccw);
}

Puzzle::~Puzzle()
{
    delete [] blocks;
}
